﻿using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;
using System.Collections.Generic;
using System.Threading;

namespace HtmlCaches
{
    // 将此文件放到项目中。在web.config的system.web节点下，httpModules节点下 添加 <add type="HtmlCaches.FileExistsHttpModule,htmlCaches" name="FileExistsHttpModule"/>    
    //BY:xuzhong;   QQ:39164105;    Email:xuzhong_718@163.com;
    //欢迎传播 更新修改 请注明来源 发现更大BUG敬请联系告之
    public class FileExistsHttpModule : IHttpModule
    {
        #region IHttpModule 成员

        /// <summary>
        /// 存放缓存的文件名
        /// </summary>
        private string htmltxtfilename;
        /// <summary>
        /// 存放缓存的绝对路径
        /// </summary>
        private string htmltxtrealpath;

        private HttpApplication appcallback;
        public void Dispose()
        {
        }

        public void Init(HttpApplication app)
        {
            app.BeginRequest += new EventHandler(app_BeginRequest);                                     //注册页请请求开始的管道事件
        }

        /// <summary>
        /// 页面请求开始时进行处理，拿到绝对路径如果是HTML放行，如果是aspx页面转化为html页面
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void app_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication app = (HttpApplication)sender;
        
            string exepath = app.Context.Request.CurrentExecutionFilePath;                                               //虚拟路径(/Default.aspx)
            string phypath = app.Context.Request.PhysicalPath;                                                           //本地绝对路径 (E:\\xuz\DataAnalysis\Default.aspx)
            string exeplypath = app.Context.Request.Url.AbsoluteUri;                                                     //取到路页在的公网地址
            
            string websiterootph = app.Server.MapPath("/");                                                                //获得网站所在的根目录；


            string updatetimediff = string.Empty;                                                                       //自动更新时间
            string htmlcache = string.Empty;                                                                            //caches开关锁 仅为open时才有效
            string saverootpath = string.Empty;                                                                         //保存的代码根目录
            string filterpage = string.Empty;                                                                           //过滤哪些页才做处理
            string listpage = string.Empty;
            StringMd5.GetParameters(out updatetimediff, out htmlcache, out saverootpath, out filterpage, out listpage);
            List<string> filterpagelist = new List<string>(filterpage.ToLower().Split(','));                                      //从配置文件中取到过滤的值存放到list中
          
            if (!filterpagelist.Contains(exepath.ToLower()))                                            //当页面不在过滤条件中时，直接跳出
            {
                return;
            }
            int updatetimediff_n = int.Parse(updatetimediff);                                                                //自动更新时间

            if (htmlcache.ToLower() == "open")                                                                          //置于config中的开关，open启用，其它为关闭
            {
                if (Path.GetExtension(exepath) == ".aspx")                                                                //判定是否是.aspx页面，
                {


                    if (exepath.ToLower() == "/index.aspx")                                                                                //当是检查站是进入
                    {


                    }

                    if (string.IsNullOrEmpty(app.Context.Request["isreversal"]))                                          //判断是否是构建下载HTML代码时造成的回环
                    {
                        string htmlpath = phypath.Replace(".aspx", ".txt");                                              //将aspx绝对路径 转换 成对应的 HTML绝对路径页面地址；
                        appcallback = app;
                        htmltxtfilename = GetFileNameMd5(exeplypath);
                        
                        htmltxtrealpath = GetFilePathMd5Type(websiterootph, saverootpath, htmltxtfilename, exeplypath);                      //html缓存的真实地址
                        if (File.Exists(htmltxtrealpath))                                                                    //如果绝对地址存在就跳转到绝对地址上去，如果不存在就走管道进入aspx页面，在aspx套接的JS上完成HTML页面的生存
                        {                                                                                                 // 存在
                            string hs = File.ReadAllText(htmltxtrealpath);                                                   // 读取 html绝对路径 内容，并将其置于http输出流中，直接输出至浏览器                         
                            string[] hsarray = hs.Split(new string[] { "@@timestamp_", "@@sourcepage_" }, StringSplitOptions.None);        //将HTML代码和时间戳分隔开  
                            string htmloutput = hsarray[0];
                            string _timestamp = hsarray[1];
                            DateTime savedt = DateTime.Parse(_timestamp);                                                 //取到时间 待比较后自动更新  当文件时间大于5分钟时将重新此页面的HTML代码
                            int difftime = Convert.ToInt32(DateTime.Now.Subtract(savedt).TotalMinutes);
                            if (difftime > updatetimediff_n)
                            {
                                //优化点，当需要自动更新时，考虑可以新开一个线程，来执行写缓存的操作
                                //  ThreadPool.QueueUserWorkItem(new WaitCallback(HtmlwriteCallback));
                                GetHTML_Write_pool(app);
                            }
                            else                                                                                           //小于更新时间，则直接将文件中的内容输出
                            {
                                app.Context.Response.Write(htmloutput);
                                app.Context.Response.End();
                            }
                        }
                        else                                                                                               //当存放HTML代码的和txt文件不存在时，调用方法生存txt文件
                        {
                            //优化点，当没有文件的时候，考虑可以新开一个线程，来执行写缓存的操作                           
                            GetHTML_Write_pool(app);
                            //  ThreadPool.QueueUserWorkItem(new WaitCallback(HtmlwriteCallback));

                        }
                    }
                }
            }



        }
        private string GetFileNameMd5(string url)
        {
            Uri uri = new Uri(url);
            string filename = "";
            if (uri.AbsolutePath.ToLower() == "/index.aspx")
            {
                filename = uri.Query == "" ? url : url.Replace(uri.Query, "");
                filename = StringMd5.ToMd5(filename);
            }
            else
            {
                filename = StringMd5.ToMd5(url);
            }
            return filename;
        }
        /// <summary>
        /// 用于多线程调用特别命名的
        /// </summary>
        /// <param name="Param"></param>
        private void HtmlwriteCallback(Object Param)
        {
            GetHTML_Write_pool(appcallback);
        }



        ////-

        /// <summary>
        /// 通过HttpApplication获取到页面HTML代码，存入到对应的文件当中。
        /// </summary>
        /// <param name="app"></param>
        /// <param name="htmlpath"></param>
        private void GetHTML_Write_pool(HttpApplication app)
        {

            string exeplypath = app.Context.Request.Url.AbsoluteUri;                                           //取到路页在的公网地址
            string _timestamp = "@@timestamp_" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");//时间戳
            string _sourcepage = "@@sourcepage_" + exeplypath;//缓存页面路径
            if (exeplypath.Contains('?'))
            {
                exeplypath = exeplypath + "&isreversal=yes";                                                   //附加上参数，当页面进入时，判断有无此参数
            }
            else
            {
                exeplypath = exeplypath + "?isreversal=yes";
            }
            WebClient wc = new WebClient();
            wc.Encoding = Encoding.UTF8;
            string htmlstring = string.Empty;                                                 //拿到页面的HTML代码  
            try
            {
                htmlstring = wc.DownloadString(exeplypath);
            }
            catch (Exception)
            {
                htmlstring = "未知错误";
            }
            if (htmlstring == "未知错误") return;
            htmlstring = htmlstring + _timestamp + _sourcepage;
            string dirpath = Path.GetDirectoryName(htmltxtrealpath);                                          //判断文件目录是否存在，不存在的话创建目录
            if (!Directory.Exists(dirpath))
            {
                Directory.CreateDirectory(dirpath);
            }

            File.WriteAllText(htmltxtrealpath, htmlstring);                                                   //写入对应的HTML页中

        }
        //-


        /// <summary>
        /// 获取静态html文件的txt存方地址，文件名以MD5形式存在的，目录以对应的aspx名
        /// </summary>
        /// <param name="htmlsavepath"></param>
        /// <param name="saverootpath"></param>
        /// <param name="filename"></param>
        /// <returns></returns>
        private string GetFilePathMd5Type(string htmlsavepath, string saverootpath, string filename)
        {
            string rootpath = Path.GetDirectoryName(htmlsavepath);                                             // 目录
            string dirpath = Path.GetFileNameWithoutExtension(htmlsavepath);                                   // aspx文件名
            string cachestxtpath = rootpath + "\\" + saverootpath + "\\";
            if (dirpath.ToLower() == "index")
            {
                cachestxtpath = cachestxtpath + filename + ".txt";
            }
            else
            {
                cachestxtpath = cachestxtpath + dirpath + "\\" + filename + ".txt";
            }

            return cachestxtpath;

        }


        /// <summary>
        /// 获取静态html文件的txt存方地址，文件名以MD5形式存在的，有新的目录结构支持多层
        /// </summary>
        /// <param name="websiterootph">网站所在根目录</param>
        /// <param name="saverootpath">存放缓存的文件夹名</param>
        /// <param name="filename">文件名</param>
        /// <param name="exeplypath">ＵＲＬ的全路径包含域名</param>
        /// <returns></returns>
        private string GetFilePathMd5Type(string websiterootph, string saverootpath, string filename, string exeplypath)
        {
            Uri uri = new Uri(exeplypath);
            string tempabph = uri.AbsolutePath;
            string tempdnshost = uri.DnsSafeHost;

            string cachestxtpath = websiterootph;                                       //拼接成最后的保存地址     
           
            cachestxtpath = cachestxtpath + saverootpath;


            if (tempabph.ToLower() == "/index.aspx")                                    //如果是首页就直接在目录+caches下生存缓存文件
            {
                cachestxtpath = cachestxtpath + "\\" + filename + ".txt";
            }
            else
            {                                                                           //其它页，则在caches下生存文件夹用对应的aspx文件名命名，
                string temp = tempabph.Replace("/", "\\").Replace(".aspx", "");
                cachestxtpath = cachestxtpath + temp;
                cachestxtpath = cachestxtpath + "\\" + filename + ".txt";
            }

            return cachestxtpath;

        }

        #endregion
    }
}
